package com.ajocer.springbootinit.rag;

import com.ajocer.springbootinit.model.entity.Spot;
import com.ajocer.springbootinit.service.SpotService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 缓存预热组件
 * 在应用启动时预热缓存，提高首次访问速度
 */
@Component
@Slf4j
public class CacheWarmer implements ApplicationRunner {

    @Autowired
    private TourismRagService tourismRagService;

    @Autowired
    private SpotService spotService;
    
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 预热常用查询场景
     */
    private static final String[] COMMON_PREFERENCES = {
            "我喜欢历史文化景点",
            "我想去海边度假",
            "适合带孩子的景点",
            "想看山水风景",
            "适合拍照的景点",
            "想体验当地美食"
    };

    @Override
    public void run(ApplicationArguments args) {
        log.info("开始预热缓存...");
        
        // 1. 预热热门景点推荐
        warmupHotSpots();
        
        // 2. 预热常用查询场景
        warmupCommonQueries();
        
        // 3. 设置缓存过期策略
        configureCacheExpiry();
        
        log.info("缓存预热完成");
    }
    
    /**
     * 预热热门景点推荐
     */
    private void warmupHotSpots() {
        try {
            // 获取访问量最高的20个景点
            List<Spot> hotSpots = getHotSpots(20);
            log.info("开始预热 {} 个热门景点的推荐结果", hotSpots.size());
            
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            
            // 检查线程池状态
            if (threadPoolExecutor.isShutdown() || threadPoolExecutor.getQueue().size() >= threadPoolExecutor.getMaximumPoolSize()) {
                log.warn("线程池已满或已关闭，跳过热门景点预热");
                return;
            }
            
            // 限制并发任务数量，每次最多提交5个任务
            int batchSize = 5;
            for (int i = 0; i < hotSpots.size(); i += batchSize) {
                int end = Math.min(i + batchSize, hotSpots.size());
                List<CompletableFuture<Void>> batchFutures = new ArrayList<>();
                
                for (int j = i; j < end; j++) {
                    Spot spot = hotSpots.get(j);
                    try {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        // 预先生成热门景点的推荐结果并缓存
                        String query = "我想去" + spot.getSpotName() + "附近游玩";
                        tourismRagService.recommendSpots(query);
                        log.debug("预热景点推荐: {}", spot.getSpotName());
                    } catch (Exception e) {
                        log.warn("预热景点推荐失败: {}", spot.getSpotName(), e);
                    }
                }, threadPoolExecutor);
                
                        batchFutures.add(future);
                    } catch (Exception e) {
                        log.warn("提交预热任务失败: {}", spot.getSpotName(), e);
                    }
                }
                
                try {
                    // 等待当前批次任务完成后再提交下一批
                    CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0]))
                            .get(1, TimeUnit.MINUTES);
                    
                    futures.addAll(batchFutures);
                } catch (Exception e) {
                    log.warn("等待批次任务完成失败", e);
                }
                
                // 在批次之间添加短暂延迟，避免线程池过载
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            
            log.info("热门景点推荐预热完成");
        } catch (Exception e) {
            log.error("热门景点预热失败", e);
        }
    }
    
    /**
     * 预热常用查询场景
     */
    private void warmupCommonQueries() {
        try {
            log.info("开始预热常用查询场景");
            
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            
            // 检查线程池状态
            if (threadPoolExecutor.isShutdown() || threadPoolExecutor.getQueue().size() >= threadPoolExecutor.getMaximumPoolSize()) {
                log.warn("线程池已满或已关闭，跳过常用查询场景预热");
                return;
            }
            
            // 限制并发任务数量，每次最多提交3个任务
            int batchSize = 3;
            for (int i = 0; i < COMMON_PREFERENCES.length; i += batchSize) {
                int end = Math.min(i + batchSize, COMMON_PREFERENCES.length);
                List<CompletableFuture<Void>> batchFutures = new ArrayList<>();
                
                for (int j = i; j < end; j++) {
                    String preference = COMMON_PREFERENCES[j];
                    try {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    try {
                        // 预先生成常用查询场景的推荐结果并缓存
                        tourismRagService.recommendSpots(preference);
                        tourismRagService.recommendRoutes(preference);
                        log.debug("预热查询场景: {}", preference);
                    } catch (Exception e) {
                        log.warn("预热查询场景失败: {}", preference, e);
                    }
                }, threadPoolExecutor);
                
                        batchFutures.add(future);
                    } catch (Exception e) {
                        log.warn("提交预热任务失败: {}", preference, e);
                    }
                }
                
                try {
                    // 等待当前批次任务完成后再提交下一批
                    CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0]))
                            .get(1, TimeUnit.MINUTES);
                    
                    futures.addAll(batchFutures);
                } catch (Exception e) {
                    log.warn("等待批次任务完成失败", e);
                }
                
                // 在批次之间添加短暂延迟，避免线程池过载
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            
            log.info("常用查询场景预热完成");
        } catch (Exception e) {
            log.error("常用查询场景预热失败", e);
        }
    }
    
    /**
     * 配置缓存过期策略
     */
    private void configureCacheExpiry() {
        // 设置不同缓存的过期时间，实现缓存淘汰策略
        redisTemplate.expire("tourismRecommendations", 24, TimeUnit.HOURS);
        redisTemplate.expire("tourismRouteRecommendations", 24, TimeUnit.HOURS);
        redisTemplate.expire("embeddings", 7, TimeUnit.DAYS);
        
        log.info("缓存过期策略配置完成");
    }
    
    /**
     * 获取热门景点
     */
    private List<Spot> getHotSpots(int limit) {
        try {
            // 查询访问量最高的景点
            return spotService.getHotSpots(limit);
        } catch (Exception e) {
            log.error("获取热门景点失败", e);
            // 如果获取失败，返回空列表
            return new ArrayList<>();
        }
    }
} 